package com.piper.lock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Collections;

/**
 * @author piper
 */
@Component
public class RedisTemplateUtil {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 无法解决 key为数组转换为字符串
     */
    public Boolean lock(String key, String value, Long second) {
        String evalScript = "if redis.call('set', KEYS[1], ARGV[1], 'NX','EX', ARGV[2]) == 'OK' then return 1 else return 0 end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(evalScript, Boolean.class);
        boolean result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value, String.valueOf(second));

        if (result) {
            return Watchdog.startWatchdog(stringRedisTemplate, key, value, second);
        }
        return false;
    }

    public Boolean setNX(String key, String value, Long second) {
        RedisSerializer valueSerializer = stringRedisTemplate.getValueSerializer();
        RedisSerializer keySerializer = stringRedisTemplate.getKeySerializer();

        boolean result = stringRedisTemplate.execute((RedisCallback<Boolean>) (connection) ->
                connection.set(keySerializer.serialize(key), valueSerializer.serialize(value),
                        Expiration.seconds(second), RedisStringCommands.SetOption.SET_IF_ABSENT));

        if (result) {
            return Watchdog.startWatchdog(stringRedisTemplate, key, value, second);
        }
        return false;
    }

    /**
     * @param key
     * @param value
     * @param millisecond 毫秒(EX设置秒PX设置毫秒)
     * @return
     */
    public Boolean setNXPX(String key, String value, Long millisecond) {
        RedisSerializer valueSerializer = stringRedisTemplate.getValueSerializer();
        RedisSerializer keySerializer = stringRedisTemplate.getKeySerializer();

        boolean result = stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            Object obj = connection.execute("set",
                    keySerializer.serialize(key), valueSerializer.serialize(value),
                    "NX".getBytes(StandardCharsets.UTF_8), "PX".getBytes(StandardCharsets.UTF_8),
                    String.valueOf(millisecond).getBytes(StandardCharsets.UTF_8));
            return obj != null;
        });

        if (result) {
            return Watchdog.startWatchdog(stringRedisTemplate, key, value, millisecond);
        }
        return false;
    }

    /**
     * 解锁脚本
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean unlock(String key, Object value) {
        String evalScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]); " +
                "else return 0; " +
                "end;";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(evalScript, Boolean.class);
        return stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value);
    }

}
